﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using TallComponents.Web.Pdf;
using TallComponents.Web.Pdf.Resources;

namespace TallComponents.Web.Storage
{
   /// <summary>
   /// Storage provider that uses session state to store PDF documents.
   /// </summary>
   public class SessionStorageProvider : PdfStorageProvider
   {
      private static readonly string storageKey = typeof( SessionStorageProvider ).FullName;

      /// <summary>
      /// Stores a file.
      /// </summary>
      /// <param name="fileName">Name of the file.</param>
      /// <param name="data">A seekable stream containing the file data.</param>
      /// <returns>The unique key for the file.</returns>
      /// <exception cref="ArgumentNullException">Occures when <paramref name="fileName"/> or <paramref name="data"/> is <c>null</c>.</exception>
      /// <remarks>The <paramref name="data"/> stream may be closed immediatly after this method returns.</remarks>
      public override string StoreFile( string fileName, Stream data )
      {
         if ( null == data )
            throw new ArgumentNullException( "data" );

         if ( string.IsNullOrEmpty( fileName ) )
            throw new ArgumentNullException( Pdf.Resources.Text.PdfStorageProvider_Empty_FileName_Not_Allowed, "fileName" );

         CachedFile cache;
         try
         {
            cache = new CachedFile( Guid.NewGuid().ToString(), fileName, data );
         }
         catch ( Exception ex )
         {
            throw new ArgumentException( Pdf.Resources.Text.PdfStorageProvider_Invalid_Document, "data", ex );
         }
         
         if ( null != Context.Session[ storageKey ] )
         {
            Context.Session.Remove( storageKey ); // remove previous file form session storage
         }
         Context.Session.Add( storageKey, cache );

         return cache.StorageKey;
      }

      /// <summary>
      /// Gets information about a PDF document.
      /// </summary>
      /// <param name="key">The storage key.</param>
      /// <returns>
      /// A <see cref="TallComponents.Web.Pdf.Document"/> object describing the document with the specified <paramref name="key"/> or <c>null</c> if the key is not valid or associated with an invalid document.
      /// </returns>
      public override Document GetDocument( string key )
      {
         var cache = Context.Session[ storageKey ] as CachedFile;
         if ( null != cache )
         {
            if ( cache.StorageKey.Equals( key, StringComparison.OrdinalIgnoreCase ) )
            {
               return cache.Document;
            }
         }
         return null;
      }


      /// <summary>
      /// Gets information on all the documents in storage.
      /// </summary>
      /// <returns>
      /// A list of <see cref="Document"/> objects, describing all PDF documents managed by this provider.
      /// </returns>
      public override IList<Document> GetDocuments()
      {
         var result = new List<Document>();
         var cache = Context.Session[ storageKey ] as CachedFile;
         if ( null != cache )
         {
            result.Add( cache.Document );
         }

         return result;
      }

      [Serializable]
      private class CachedFile
      {
         public CachedFile( string key, string fileName, Stream data )
         {
            FileName = fileName;
            StorageKey = key;
            _data = new byte[ data.Length ];
            if ( data.CanSeek )
            {
               data.Seek( 0, SeekOrigin.Begin );
            }
            data.Read( _data, 0, ( int )data.Length );
            try
            {
               _document = new Document( new MemoryStream( _data ), FileName, StorageKey );
            }
            catch ( Exception ex )
            {
               throw new ArgumentException( Pdf.Resources.Text.PdfStorageProvider_Invalid_Document, "data", ex );
            }
         }

         public string FileName { get; private set; }
         public string StorageKey { get; private set; }
         public Document Document
         {
            get
            {
               if ( null == _document)
               {
                  _document = new Document( new MemoryStream( _data ), FileName, StorageKey );
               }
               return _document;
            }
         }

         private byte[] _data;
         [NonSerialized]
         private Document _document;
         
      }

#if NET35 || NET40
      /// <summary>
      /// Gets the http context.
      /// </summary>
      /// <value>The context.</value>
      protected virtual HttpContextBase Context
      {
         get
         {
            if ( null == HttpContext.Current )
               throw new InvalidOperationException( Text.SessionStorageProvider_not_availale_in_this_context );

            return new HttpContextWrapper( HttpContext.Current );
         }
      }
#else
      private HttpContext Context
      {
         get
         {
            if ( null == HttpContext.Current )
               throw new InvalidOperationException( Text.SessionStorageProvider_not_availale_in_this_context );

            return HttpContext.Current;
         }
      }
#endif
      
   }
}
